#include "lv_port_disp.h"

#include "FreeRTOS.h"
#include "task.h"

#include "board.h"

#define LV_HOR_RES_MAX  LCD_X_SIZE
#define LV_VER_RES_MAX  LCD_Y_SIZE

static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
static int32_t x1_flush;
static int32_t y1_flush;
static int32_t x2_flush;
static int32_t y2_fill;
static int32_t y_fill_act;
static const lv_color_t *buf_to_flush;
static lv_disp_t *our_disp = NULL;
static lv_disp_drv_t disp_drv;
extern uint16_t *tli_buff1;
#define LCD_FRAME_BUF_ADDR 0XC0000000
#define TLI_LCD_FRAMEBUF_SIZE (480*480*2)
#define COLOR_BUF_SIZE (LV_HOR_RES_MAX*LV_VER_RES_MAX)
static lv_color_t color_buf2[COLOR_BUF_SIZE]__attribute__(( section("sdram_area")));
/*!
 \brief dma transfer data
 \param[in] src_addr: source address
 \param[in] dst_addr: destination address
 \param[in] datalength: data length
 \param[out] none
 \retval none
*/
void dma_transfer(uint32_t src_addr, uint32_t dst_addr, uint32_t datalength)
{
    dma_periph_address_config(DMA0, DMA_CH1, src_addr);
    dma_memory_address_config(DMA0, DMA_CH1, DMA_MEMORY_0, dst_addr);
    dma_transfer_number_config(DMA0, DMA_CH1, datalength);
    dma_interrupt_disable(DMA0, DMA_CH1, DMA_CHXCTL_HTFIE);
    dma_interrupt_enable(DMA0, DMA_CH1, DMA_CHXCTL_FTFIE);
    dma_channel_enable(DMA0, DMA_CH1);
}

void normal_transfer(void)
{
    while(1) {
        memcpy(&tli_buff1[y_fill_act*LV_HOR_RES_MAX + x1_flush], buf_to_flush, (x2_flush - x1_flush + 1)*2);   
        y_fill_act ++;
        if(y_fill_act > y2_fill) {
            lv_disp_flush_ready(&disp_drv);
            return;
        } else {
            buf_to_flush += x2_flush - x1_flush + 1;
        }
    }
}

void lv_port_disp_init(void)
{
    static lv_disp_draw_buf_t buf;
    lv_disp_draw_buf_init(&buf, color_buf2, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX);
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf = &buf;
    disp_drv.flush_cb = disp_flush;
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;
    lv_disp_drv_register(&disp_drv);
}
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
    /*Return if the area is out the screen*/
    if(area->x2 < 0 || area->y2 < 0 || area->x1 > LV_HOR_RES_MAX - 1 || area->y1 > LV_VER_RES_MAX - 1) {
        return;
    }

    /*Truncate the area to the screen*/
    int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
    int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
    int32_t act_x2 = area->x2 > LV_HOR_RES_MAX - 1 ? LV_HOR_RES_MAX - 1 : area->x2;
    int32_t act_y2 = area->y2 > LV_VER_RES_MAX - 1 ? LV_VER_RES_MAX - 1 : area->y2;
    x1_flush = act_x1;
    y1_flush = act_y1;
    x2_flush = act_x2;
    y2_fill = act_y2;
    y_fill_act = act_y1;
    buf_to_flush = color_p;
    //dma_transfer((uint32_t)buf_to_flush, (uint32_t)&tli_buff1[y_fill_act * LV_HOR_RES_MAX + x1_flush],(x2_flush - x1_flush + 1));
    normal_transfer();
}

/* this function handles DMA1 Handler exception */
void DMA0_Channel2_IRQHandler(void)
{
    if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF)) {
        dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
        y_fill_act ++;
        if(y_fill_act > y2_fill) {
            lv_disp_flush_ready(&disp_drv);
        } else {
            buf_to_flush += x2_flush - x1_flush + 1;
            dma_transfer((uint32_t)buf_to_flush, (uint32_t)&tli_buff1[y_fill_act * 
                LV_HOR_RES_MAX + x1_flush], (x2_flush - x1_flush + 1));
        }
    }
}
